#pragma once
#include "../../core/Object.h"
#include "../EUniformType.h"
POCELAIN_NAMESPACE_BEGIN
enum class EOpenGLBufferTarget : GLenum {
    ArrayBuffer             =   GL_ARRAY_BUFFER,
    AtomicCounterBuffer     =   GL_ATOMIC_COUNTER_BUFFER,
    CopyReadBuffer          =   GL_COPY_READ_BUFFER,
    CopyWriteBuffer         =   GL_COPY_WRITE_BUFFER,
    DispatchIndirectBuffer  =   GL_DISPATCH_INDIRECT_BUFFER,
    DrawIndirectBuffer      =   GL_DRAW_INDIRECT_BUFFER,
    ElementArrayBuffer      =   GL_ELEMENT_ARRAY_BUFFER,
    PixelPackBuffer         =   GL_PIXEL_PACK_BUFFER,
    PixelUnpackBuffer       =   GL_PIXEL_UNPACK_BUFFER,
    QueryBuffer             =   GL_QUERY_BUFFER,
    ShaderStorageBuffer     =   GL_SHADER_STORAGE_BUFFER,
    TextureBuffer           =   GL_TEXTURE_BUFFER,
    TransformFeedbackBuffer =   GL_TRANSFORM_FEEDBACK_BUFFER,
    UniformBuffer           =   GL_UNIFORM_BUFFER,
};
enum class EOpenGLBufferUsagePattern : GLuint {
    StreamDraw      =   GL_STREAM_DRAW,
    StreamRead      =   GL_STREAM_READ,
    StreamCopy      =   GL_STREAM_COPY,
    StaticDraw      =   GL_STATIC_DRAW,
    StaticRead      =   GL_STATIC_READ,
    StaticCopy      =   GL_STATIC_COPY,
    DynamicDraw     =   GL_DYNAMIC_DRAW,
    DynamicRead     =   GL_DYNAMIC_READ,
    DynamicCopy     =   GL_DYNAMIC_COPY,
};
class OpenGLBuffer : public Object
{
public:
    OpenGLBuffer();
    virtual ~OpenGLBuffer();
    OpenGLBuffer(const OpenGLBuffer&) = delete;
    void bind();
    void unbind();
    void setTarget(EOpenGLBufferTarget target){ mTarget=static_cast<EOpenGLBufferTarget>(target);}
    EOpenGLBufferTarget getTarget()const {return mTarget;}
    void setUsagePattern(EOpenGLBufferUsagePattern pattern){mUsage = static_cast<EOpenGLBufferUsagePattern>(pattern);}
    EOpenGLBufferUsagePattern getUsagePattern()const {return mUsage;}
    GLuint getBufferName()const{return mBufferName;}
    void write(const void *data, size_t byteCount,size_t offset = 0);
    bool read(void *data, size_t byteCount ,size_t offset = 0);

    size_t getitemSize() const;
    void setItemSize(size_t newItemSize);

    bool getNormalized() const
    {
        return mNormalized;
    }
    void setNormalzied(bool val)
    {
        mNormalized = val;
    }
    GLenum getGLType()const
    {
        return mGLType;
    }
    void setGLType(GLenum val)
    {
        mGLType = val;
    }
    void setGLTypeByUniformType(EUniformType utype)
    {
        GLenum type = GL_FLOAT;

        switch(utype)
        {
        //boolean
        case EUniformType::E_UNIFORM_BOOL:
            type = GL_BOOL;
            break;
            // integer
        case EUniformType::E_UNIFORM_BYTE:
            type = GL_BYTE;
            break;
        case EUniformType::E_UNIFORM_UBYTE:
            type = GL_UNSIGNED_BYTE;
            break;
        case EUniformType::E_UNIFORM_SHORT:
            type = GL_SHORT;
            break;
        case EUniformType::E_UNIFORM_USHORT:
            type = GL_UNSIGNED_SHORT;
            break;
        case EUniformType::E_UNIFORM_INT:
            type = GL_INT;
            break;
        case EUniformType::E_UNIFORM_UINT:
            type = GL_UNSIGNED_INT;
            break;
        case EUniformType::E_UNIFORM_FLOAT:
            type = GL_FLOAT;
            break;
        case EUniformType::E_UNIFORM_DOUBLE:
            type = GL_DOUBLE;
            break;
            //vector
        case EUniformType::E_UNIFORM_VECTOR2:
            type = GL_FLOAT_VEC2;
            break;
        case EUniformType::E_UNIFORM_VECTOR3:
            type = GL_FLOAT_VEC3;
            break;
        case EUniformType::E_UNIFORM_VECTOR4:
            type = GL_FLOAT_VEC4;
            break;
        case EUniformType::E_UNIFORM_DVECTOR2:
            type = GL_DOUBLE_VEC2;
            break;
        case EUniformType::E_UNIFORM_DVECTOR3:
            type = GL_DOUBLE_VEC3;
            break;
        case EUniformType::E_UNIFORM_DVECTOR4:
            type = GL_DOUBLE_VEC4;
            break;
        case EUniformType::E_UNIFORM_IVECTOR2:
            type = GL_INT_VEC2;
            break;
        case EUniformType::E_UNIFORM_IVECTOR3:
            type = GL_INT_VEC3;
            break;
        case EUniformType::E_UNIFORM_IVECTOR4:
            type = GL_INT_VEC4;
            break;
        case EUniformType::E_UNIFORM_UVECTOR2:
            type = GL_UNSIGNED_INT_VEC2;
            break;
        case EUniformType::E_UNIFORM_UVECTOR3:
            type = GL_UNSIGNED_INT_VEC3;
            break;
        case EUniformType::E_UNIFORM_UVECTOR4:
            type = GL_UNSIGNED_INT_VEC4;
            break;
        case EUniformType::E_UNIFORM_BVECTOR2:
            type = GL_BOOL_VEC2;
            break;
        case EUniformType::E_UNIFORM_BVECTOR3:
            type = GL_BOOL_VEC3;
            break;
        case EUniformType::E_UNIFORM_BVECTOR4:
            type = GL_BOOL_VEC4;
            break;
            //matrix
        case EUniformType::E_UNIFORM_MATRIX2x2:
            type = GL_FLOAT_MAT2;
            break;
        case EUniformType::E_UNIFORM_MATRIX2x3:
            type = GL_FLOAT_MAT2x3;
            break;
        case EUniformType::E_UNIFORM_MATRIX2x4:
            type = GL_FLOAT_MAT2x4;
            break;

        case EUniformType::E_UNIFORM_MATRIX3x2:
            type = GL_FLOAT_MAT3x2;
            break;
        case EUniformType::E_UNIFORM_MATRIX3x3:
            type = GL_FLOAT_MAT3;
            break;
        case EUniformType::E_UNIFORM_MATRIX3x4:
            type = GL_FLOAT_MAT3x4;
            break;

        case EUniformType::E_UNIFORM_MATRIX4x2:
            type = GL_FLOAT_MAT4x2;
            break;
        case EUniformType::E_UNIFORM_MATRIX4x3:
            type = GL_FLOAT_MAT4x3;
            break;
        case EUniformType::E_UNIFORM_MATRIX4x4:
            type = GL_FLOAT_MAT4;
            break;
        case EUniformType::E_UNIFORM_DMATRIX2x2:
            type = GL_DOUBLE_MAT2;
            break;
        case EUniformType::E_UNIFORM_DMATRIX2x3:
            type = GL_DOUBLE_MAT2x3;
            break;
        case EUniformType::E_UNIFORM_DMATRIX2x4:
            type = GL_DOUBLE_MAT2x4;
            break;
        case EUniformType::E_UNIFORM_DMATRIX3x2:
            type = GL_DOUBLE_MAT3x2;
            break;
        case EUniformType::E_UNIFORM_DMATRIX3x3:
            type = GL_DOUBLE_MAT3;
            break;
        case EUniformType::E_UNIFORM_DMATRIX3x4:
            type = GL_DOUBLE_MAT3x4;
            break;
        case EUniformType::E_UNIFORM_DMATRIX4x2:
            type = GL_DOUBLE_MAT4x2;
            break;
        case EUniformType::E_UNIFORM_DMATRIX4x3:
            type = GL_DOUBLE_MAT4x3;
            break;
        case EUniformType::E_UNIFORM_DMATRIX4x4:
            type = GL_DOUBLE_MAT4;
            break;
        default:
            POCELAIN_ASSERT(false);
            break;
        }
        mGLType = type;
    }
private:
    EOpenGLBufferTarget mTarget = EOpenGLBufferTarget::ArrayBuffer;
    EOpenGLBufferUsagePattern mUsage = EOpenGLBufferUsagePattern::StaticDraw;
    GLuint mBufferName = GL_INVALID_VALUE;
    size_t mItemSize = 1;
    bool mNormalized = false;
    GLenum mGLType = 0 ;
};
POCELAIN_NAMESPACE_END
